package com.ticket.sass.admin.business.impl;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.ticket.sass.admin.business.CinemaConfigBusiness;
import com.ticket.sass.admin.dto.input.config.*;
import com.ticket.sass.admin.dto.output.TicketCheckConfigOutput;
import com.ticket.sass.admin.dto.output.config.*;
import com.ticket.sass.admin.model.KeyInfoModel;
import com.ticket.sass.admin.service.impl.*;
import com.ticket.sass.admin.util.*;
import com.ticket.sass.common.entity.tenant.*;
import com.ticket.sass.common.entity.tenant.enums.NoticePosition;
import com.ticket.sass.common.entity.tenant.enums.TicketRefundConfigType;
import com.ticket.sass.common.exception.enums.ErrorCode;
import com.ticket.sass.common.exception.exception.BadRequestException;
import lombok.AllArgsConstructor;
import org.modelmapper.TypeToken;
import org.postgresql.geometric.PGpoint;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.function.Consumer;
import java.util.stream.Collectors;

/**
 * @author ywb
 * @date 2024-03-24 17:35
 */
@Service
@AllArgsConstructor
@Transactional(rollbackFor = Exception.class)
public class CinemaConfigBusinessImpl implements CinemaConfigBusiness {
    final FileUtil fileUtil;
    final ModelUtil modelUtil;
    final CinemaServiceImpl service;
    final NoticeServiceImpl noticeService;
    final WxaConfigServiceImpl wxaConfigService;
    final WxPayConfigServiceImpl wxPayConfigService;
    final ThemeConfigServiceImpl themeConfigService;
    final CinemaConfigServiceImpl cinemaConfigService;
    final CinemaExtendServiceImpl cinemaExtendService;
    final ServiceChargeServiceImpl serviceChargeService;
    final NoticePositionServiceImpl noticePositionService;
    final TicketRefundRuleServiceImpl ticketRefundRuleService;
    final TicketCheckConfigServiceImpl ticketCheckConfigService;
    final TicketRefundConfigServiceImpl ticketRefundConfigService;


    @Override
    public WxaConfigOutput wxaConfig() {
        WxaConfigEntity entity = wxaConfigService.getOne(new QueryWrapper<WxaConfigEntity>().last("limit 1"));
        if (Objects.isNull(entity)){
            return new WxaConfigOutput(wxaConfigService.getOne(new QueryWrapper<WxaConfigEntity>().eq("cinema_id", 0).last("limit 1")));
        } else {
            return new WxaConfigOutput(entity);
        }
    }

    @Override
    public TicketRefundConfigOutput ticketRefundConfig(TicketRefundTypeInput input) {
        // 获取影院ID
        Long cinemaId = CinemaIdUtil.getCinemaIdFromRequest();
        return retrieveTicketRefundConfig(cinemaId, input.getType());
    }
    
    @Override
    public void updTicketRefundConfig(TicketRefundConfigInput input) {
        // 获取影院ID
        Long cinemaId = CinemaIdUtil.getCinemaIdFromRequest();
        validateId(cinemaId);
        
        // 查询指定类型的退票配置信息
        TicketRefundConfigEntity config = ticketRefundConfigService.getOne(new QueryWrapper<TicketRefundConfigEntity>().eq("cinema_id", cinemaId).eq("type", input.getType()));
        
        // 判断是否存在对应类型的配置信息
        if (Objects.isNull(config)) {
            // 若不存在，则创建新的配置信息
            createNewTicketRefundConfig(cinemaId, input);
        } else {
            // 若存在，则更新现有的配置信息
            updateExistingTicketRefundConfig(cinemaId, input, config);
        }
    }
    
    @Override
    public CinemaConfigOutput cinemaConfig() {
        // 获取影院ID
        Long cinemaId = CinemaIdUtil.getCinemaIdFromRequest();
        // 验证影院ID有效性
        validateId(cinemaId);
        
        // 获取影院实体
        CinemaEntity cinemaEntity = service.getById(cinemaId);
        
        // 构造输出对象
        CinemaConfigOutput output = modelUtil.mapToMap(cinemaEntity, CinemaConfigOutput.class);

        output.setContact(Arrays.asList(getCinemaContact(cinemaId)));
        // 处理通知
        List<NoticeEntity> notices = noticeService.list(new QueryWrapper<NoticeEntity>().eq("cinema_id", cinemaId));
        List<NoticesConfigOutput> output1 = modelUtil.listToList(notices, new TypeToken<List<NoticesConfigOutput>>(){}.getType());
        output1.forEach(o -> o.setPosition(noticePositionService.getPositionArrayByNoticeId(o.getId())));
        
        output.setNotices(output1);
        
        return output;
    }
    
    @Override
    public void updCinemaConfig(CinemaConfigInput input) {
        // 获取影院ID
        Long cinemaId = CinemaIdUtil.getCinemaIdFromRequest();
        // 验证影院ID有效性
        validateId(cinemaId);
        CinemaExtendEntity cinemaExtendsEntity = cinemaExtendService.getOne(new QueryWrapper<CinemaExtendEntity>().eq("cinema_id", cinemaId));
        if (cinemaExtendsEntity == null) {
            CinemaExtendEntity cinemaExtends = new CinemaExtendEntity(cinemaId, String.join(",", input.getContact()));
            cinemaExtendService.save(cinemaExtends);
        } else {
            cinemaExtendsEntity.setContact(String.join(",", input.getContact()));
            cinemaExtendService.updateById(cinemaExtendsEntity);
        }
        CinemaEntity cinemaEntity = modelUtil.mapToMap(input, CinemaEntity.class);
        
        try {
            PGpoint pGpoint = new PGpoint(cinemaEntity.getLng() + "," + cinemaEntity.getLat());
            cinemaEntity.setPoint(pGpoint);
        } catch (Exception ignored){
        
        }
        
        service.updateById(cinemaEntity);
        
        if (input.getNotices().isEmpty()){
            noticeService.remove(new QueryWrapper<NoticeEntity>().eq("cinema_id", cinemaId));
        } else {
            noticeService.remove(new QueryWrapper<NoticeEntity>().eq("cinema_id", cinemaId));
            input.getNotices().forEach(x -> {
                NoticeEntity noticeEntity = new NoticeEntity(x.getContent(), cinemaId);
                noticeService.save(noticeEntity);
                
                List<NoticePositionEntity> collect = x.getPosition().stream().map(position -> new NoticePositionEntity(noticeEntity.getId(), NoticePosition.Companion.valueOf(position))).collect(Collectors.toList());
                noticePositionService.saveBatch(collect);
            });
        }
    }
    
    // 获取影院扩展信息
    private String[] getCinemaContact(Long cinemaId) {
        CinemaExtendEntity cinemaExtendsEntity = cinemaExtendService.getOne(new QueryWrapper<CinemaExtendEntity>().eq("cinema_id", cinemaId));
        if (cinemaExtendsEntity == null) {
            return new String[0];
        }
        
        if (StrUtil.isEmpty(cinemaExtendsEntity.getContact())) {
            return new String[0];
        }
        return cinemaExtendsEntity.getContact().split(",");
    }
    
    
    /**
     * 创建新的退票配置信息
     * @param cinemaId 影院ID
     * @param input 输入的退票配置信息
     */
    private void createNewTicketRefundConfig(Long cinemaId, TicketRefundConfigInput input) {
        TicketRefundConfigEntity config = modelUtil.mapToMap(input, TicketRefundConfigEntity.class);
        config.setType(TicketRefundConfigType.Companion.valueOf(input.getType()));
        config.setCinemaId(cinemaId);
        ticketRefundConfigService.save(config);
        
        if (input.getEnable()) {
            Long configId = config.getId();
            if (Objects.nonNull(input.getTicketRefundRules())) {
                List<TicketRefundRuleEntity> list = new ArrayList<>(); // Initialize the list to hold created entities
                input.getTicketRefundRules().forEach(rule -> {
                    // Create a new TicketRefundRuleEntity for each rule in the input
                   list.add(createTicketRefundRuleEntity(cinemaId, configId, rule));
                });
                // Save the list of entities in batch
                ticketRefundRuleService.saveBatch(list);
            }
        }
    }
    
    /**
     * 更新现有的退票配置信息
     * @param cinemaId 影院ID
     * @param input 输入的退票配置信息
     * @param config 现有的退票配置信息
     */
    private void updateExistingTicketRefundConfig(Long cinemaId, TicketRefundConfigInput input, TicketRefundConfigEntity config) {
        config.setMax(input.getMax());
        config.setType(TicketRefundConfigType.Companion.valueOf(input.getType()));
        config.setEnable(input.getEnable());
        config.setInstructions(input.getInstructions());
        ticketRefundConfigService.updateById(config);
        
        // 如果启用了退票规则，则更新相关规则信息
        if (input.getEnable()) {
            Long configId = config.getId();
            if (Objects.nonNull(input.getTicketRefundRules())) {
                // 先删除旧的规则，再保存新的规则
                ticketRefundRuleService.remove(new QueryWrapper<TicketRefundRuleEntity>().eq("cinema_id", cinemaId).eq("config_id", configId));
                List<TicketRefundRuleEntity> list = new ArrayList<>();
                input.getTicketRefundRules().forEach(rule -> {
                    // Create a new TicketRefundRuleEntity for each rule in the input
                    list.add(createTicketRefundRuleEntity(cinemaId, configId, rule));
                });
                ticketRefundRuleService.saveBatch(list);
            } else {
                ticketRefundRuleService.remove(new QueryWrapper<TicketRefundRuleEntity>().eq("cinema_id", cinemaId).eq("config_id", configId));
            }
        }
    }
    
    private void validateId(Long id) {
        if (service.isEmpty(id)) {
            throw new BadRequestException(
                ErrorCode.RECORD_NOT_EXIST,
                EscapeUtil.errorMessage("id", ErrorCode.RECORD_NOT_EXIST.getReasonPhrase())
            );
        }
    }
    
    /**
     * 查询退票配置信息
     * @param cinemaId 影院ID
     * @param type 配置类型
     * @return 退票配置信息
     */
    private TicketRefundConfigOutput retrieveTicketRefundConfig(Long cinemaId, Short type) {
        TicketRefundConfigEntity config = ticketRefundConfigService.getOne(new QueryWrapper<TicketRefundConfigEntity>().eq("cinema_id", cinemaId).eq("type", type));
        TicketRefundConfigOutput output;
        
        if (config == null) {
            output = new TicketRefundConfigOutput(type);
        } else {
            output = new TicketRefundConfigOutput(config);
            List<TicketRefundRuleEntity> list = ticketRefundRuleService.list(new QueryWrapper<TicketRefundRuleEntity>().eq("cinema_id", cinemaId).eq("config_id", config.getId()));
            output.setTicketRefundRules(list.stream().map(TicketRefundRuleOutput::new).collect(Collectors.toList()));
        }
        
        return output;
    }
    
    
    @Override
    public void updWxaConfig(WxaConfigInput input) {
        Long cinemaId = Long.valueOf(ResponseUtil.getRequest().getHeader("CinemaId"));
        validateId(cinemaId);
        
        fileUtil.tempFieldPro(input, "homeShareCover");
        WxaConfigEntity config = wxaConfigService.getOne(new QueryWrapper<WxaConfigEntity>().eq("cinema_id", cinemaId));
        if (Objects.isNull(config)){
            config = modelUtil.mapToMap(input, WxaConfigEntity.class);
            config.setCinemaId(cinemaId);
            wxaConfigService.save(config);
        } else {
            config.setHomeShareContent(input.getHomeShareContent());
            config.setHomeShareCover(input.getHomeShareCover());
            config.setPrivacyPolicyContent(input.getPrivacyPolicyContent());
            config.setPrivacyPolicyTitle(input.getPrivacyPolicyTitle());
            wxaConfigService.updateById(config);
        }
    }
    
    @Override
    public ThemeConfigOutput themeConfig() {
        Long cinemaId = CinemaIdUtil.getCinemaIdFromRequest();
        ThemeConfigEntity config = themeConfigService.getOne(new QueryWrapper<ThemeConfigEntity>().eq("cinema_id", cinemaId));
        if (Objects.isNull(config)){
            ThemeConfigEntity temp = themeConfigService.getOne(new QueryWrapper<ThemeConfigEntity>().eq("cinema_id", 0));
            return ThemeConfigOutput.fromJson(temp.getData());
        }
        return ThemeConfigOutput.fromJson(config.getData());
    }
    
    @Override
    public void updThemeConfig(ThemeConfigInput input) {
        Long cinemaId = Long.valueOf(ResponseUtil.getRequest().getHeader("CinemaId"));
        ThemeConfigEntity config = themeConfigService.getOne(new QueryWrapper<ThemeConfigEntity>().eq("cinema_id", cinemaId));
        if (Objects.isNull(config)){
            config = new ThemeConfigEntity(input.toString(), cinemaId);
            themeConfigService.save(config);
        } else {
            config.setData(input.toString());
            themeConfigService.updateById(config);
        }
    }
    
    @Override
    public ServiceChargeOutput serviceCharge() {
        Long cinemaId = CinemaIdUtil.getCinemaIdFromRequest();
        validateId(cinemaId);
        ServiceChargeEntity config = serviceChargeService.getOne(new QueryWrapper<ServiceChargeEntity>().eq("cinema_id", cinemaId));
        return config == null ? new ServiceChargeOutput() : new ServiceChargeOutput(config);
    }
    
    @Override
    public void updServiceCharge(ServiceChargeInput input) {
        Long cinemaId = Long.valueOf(ResponseUtil.getRequest().getHeader("CinemaId"));
        validateId(cinemaId);
        ServiceChargeEntity config = serviceChargeService.getOne(new QueryWrapper<ServiceChargeEntity>().eq("cinema_id", cinemaId));
        if (Objects.isNull(config)){
            config = modelUtil.mapToMap(input, ServiceChargeEntity.class);
            config.setCinemaId(cinemaId);
            serviceChargeService.save(config);
        } else {
            ServiceChargeEntity entity = modelUtil.mapToMap(input, ServiceChargeEntity.class);
            entity.setId(config.getId());
            serviceChargeService.updateById(entity);
        }
    }
    
    @Override
    public TicketCheckConfigOutput ticketCheckConfig() {
        TicketCheckConfigEntity config = ticketCheckConfigService.getOne(new QueryWrapper<TicketCheckConfigEntity>()
            .select("id", "is_only", "is_recommend", "is_default", "cinema_id", "tenant_id", "created_at", "updated_at")
            .last("limit 1"));
        return config == null ? new TicketCheckConfigOutput() : new TicketCheckConfigOutput(config);
    }
    
    @Override
    public WxPayConfigOutput wxPayConfig() {
        // 获取当前微信支付配置
        WxPayConfigEntity config = wxPayConfigService.getOne(new QueryWrapper<WxPayConfigEntity>().last("limit 1"));
        // 将配置转换为输出对象
        return config == null ? new WxPayConfigOutput() : new WxPayConfigOutput(config);
    }
    
    
    @Override
    public void updTicketCheckConfig(TicketCheckConfigInput input) {
        TicketCheckConfigEntity config = ticketCheckConfigService.getOne(new QueryWrapper<TicketCheckConfigEntity>()
            .select("id", "is_only", "is_recommend", "is_default", "cinema_id", "tenant_id", "created_at", "updated_at")
            .last("limit 1"));
        
        TicketCheckConfigEntity newConfig = new TicketCheckConfigEntity(input.getOnly(), input.getRecommend(), input.getEnableDefault());
        
        if (ObjectUtil.isNull(config)) {
            // 如果配置为空，直接保存新的配置
            ticketCheckConfigService.save(newConfig);
        } else {
            // 如果配置不为空，更新现有配置
            newConfig.setId(config.getId());
            ticketCheckConfigService.updateById(newConfig);
        }
    }
    

    @Override
    public void updWxPayConfig(WxPayConfigInput input) {
        // 创建新的配置对象
        WxPayConfigEntity config = new WxPayConfigEntity(input.getMchId(), input.getMchKey());
        
        // 如果当前配置不为空，保留其id
        WxPayConfigEntity existingConfig = wxPayConfigService.getOne(new QueryWrapper<WxPayConfigEntity>().last("limit 1"));
        if (existingConfig != null) {
            config.setId(existingConfig.getId());
        }
        
        // 获取上传的p12文件
        MultipartFile p12File = input.getFile();
        
        // 如果需要更新p12文件，解析并保存p12文件内容
        if (input.getFileUpdate()) {
            String p12Content = fileUtil.parseP12Certificate(input.getMchId(), p12File);
            config.setP12Content(p12Content);
        }
        
        // 保存或更新配置
        wxPayConfigService.saveOrUpdate(config);
    }
    
    @Override
    public void setCinemaQrcode(CinemaQrCodeInput input) {
        fileUtil.tempFieldPro(input, "wxGroupQrcode");
        fileUtil.tempFieldPro(input, "wxOfficialAccountQrcode");
        cinemaConfigService.setQrCode("wx_group_qrcode", input.getWxGroupQrcode());
        cinemaConfigService.setQrCode("wx_official_account_qrcode", input.getWxOfficialAccountQrcode());
    }
    
    @Override
    public CinemaQrCodeOutput getCinemaQrcode() {
        CinemaQrCodeOutput output = new CinemaQrCodeOutput();
        setQrCodeModel(output::setWxGroupModel, "wx_group_qrcode");
        setQrCodeModel(output::setWxOfficialAccountQrcodeModel, "wx_official_account_qrcode");
        return output;
    }
    
    private void setQrCodeModel(Consumer<KeyInfoModel> setter, String key) {
        String qrCode = cinemaConfigService.getQrCode(key);
        if (StrUtil.isNotEmpty(qrCode)) {
            setter.accept(new KeyInfoModel(qrCode));
        }
    }

    
    private TicketRefundRuleEntity createTicketRefundRuleEntity(Long cinemaId, Long configId, TicketRefundRuleInput rule) {
        return new TicketRefundRuleEntity(
            rule.getTakeTicketStatus(),
            rule.getStartScope(),
            rule.getDuration(),
            rule.getDurationScope(),
            rule.getScope(),
            rule.getServiceCharge(),
            configId,
            cinemaId
        );
    }
}
